from machine import Pin


READ_CODE = 0b110             # 读模式
WRITE_CODE = 0b101            # 写模式
COMMAND_CODE = 0b100          # 命令模式

# LCD OFF: 10000000010X(高位100表示命令, 最后一位X为无效bit)
# LCD ON:  10000000011X
# BIAS & COM: 1000010abXcX: c: 0(可选1/2偏压), 1: 可选(1/3偏压)
#                           ab: 00(可选2个公共口), 01(可选3个公共口), 10(可选4个公共口)
SYS_DIS = 0x0                # 关闭震荡器(0b0)
SYS_EN = 0x2                 # 开启振荡器(0b10)
LCD_OFF = 0x4                # 关闭显示(0b100)
LCD_ON = 0x6                 # 开启显示(0b110)
RC256 = 0x30                 # 使用内部震荡器RC(0b110000)
BIAS = 0x52                  # 设置1/3偏压和4个公共口(0b1010010)

# 编写映射关系
segments = {
    "0": 0b01111101,
    "1": 0b00000101,
    "2": 0b01011011,
    "3": 0b00011111,
    "4": 0b00100111,
    "5": 0b00111110,
    "6": 0b01111110,
    "7": 0b00010101,
    "8": 0b01111111,
    "9": 0b00111111,
    "A": 0b01110111,
    "b": 0b01101110,
    "C": 0b01111000,
    "d": 0b01001111,
    "E": 0b01111100,
    "F": 0b01111000,
    "-": 0b00000010
}


class TM1621:
    """
    功能: 该类用于实现TM1621的驱动程序
    说明: 这里的rd管脚为可选, 可以设置为None
    """
    def __init__(self, cs, rd, wr, data):
        # 定义TM1621B通信引脚(根据实际接线调整)
        self.CS = Pin(cs, Pin.OUT)
        self.RD = Pin(rd, Pin.OUT) if rd else None
        self.WR = Pin(wr, Pin.OUT)
        self.data = data
        self.DATA = Pin(data, Pin.OUT)
        self.init()
        self.clear()

    # 功能: 初始化
    def init(self):
        self.CS.value(0)            # 芯片激活需要CS上升沿, 所以这里构造一个上升沿
        for pin in [self.CS, self.WR, self.DATA]:
            pin.value(1)

        for cmd in [BIAS, RC256, SYS_DIS, SYS_EN, LCD_ON]:
            self.write_command(cmd)

    # 功能: 发送count个bit, 按照从高位到低位依次发送
    def send_bits(self, bits, count=1):
        for i in range(count):
            self.WR.value(0)
            self.DATA.value(1 if bits & (1 << (count - i - 1)) else 0)
            self.WR.value(1)

    # 功能: 读取length长度的数据
    def receive_data(self, length=4):
        ret = 0
        self.DATA = Pin(self.data, Pin.IN)
        for i in range(length):
            self.RD.value(0)
            ret += self.DATA.value() << (3 - i)
            self.RD.value(1)
        self.DATA = Pin(self.data, Pin.OUT)

        return ret

    # 功能: 写入或读取数据
    # 参数:
    #   code: 代码类型
    #   addr: 地址 或 命令
    #   value: 写入的数据或读取的数据
    def write(self, code, addr, value=None, length=None):
        ret = None
        if code not in [READ_CODE, WRITE_CODE, COMMAND_CODE]:
            print("Invalid code:", code)
            return

        iter_items = [(3, code)]
        if code in [WRITE_CODE, READ_CODE]:
            iter_items += [(6, addr), (length or 4, value)]
        elif code == COMMAND_CODE:
            iter_items += [(9, addr)]

        self.CS.value(0)
        for length, item in iter_items:
            if item is not None:
                self.send_bits(item, length)
            else:
                ret = self.receive_data(length)
        self.CS.value(1)

        return ret

    # 功能: 写入命令
    def write_command(self, cmd, length=None):
        self.write(COMMAND_CODE, cmd, length)

    # 功能: 向指定地址写入数据
    def write_data(self, addr, data, length=8):
        self.write(WRITE_CODE, addr, data, length)

    def read_data(self, addr):
        return self.write(READ_CODE, addr)

    # 功能: 清空显示
    def clear(self):
        self.write_data(0, 0, 32)

    # 功能: 显示最多4位段码, 从最低位开始向高位显示
    def display(self, data):
        data = str(data)
        buf, idx = 0, 0
        while data:
            offset = -2 if data[-2:-1] == "." else -1
            seg_code = segments.get(data[-1:], 0)
            if offset == -2:
                seg_code = seg_code | 0x80          # 增加点的编码

            buf += (seg_code << (idx * 8))
            data = data[:offset]
            idx += 1
            if idx >= 4:
                break

        self.write_data(0, buf, 32)


if __name__ == '__main__':
    tm = TM1621(4, None, 17, 5)
    tm.init()
    tm.display("1.2.4.5")
